package query;

import index.Document;
import index.Term;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Scanner;
import java.util.Set;

import utils.Logger;
import utils.Util;

public class Querier
{
    private Map<Integer, Double> pageRanks;

    public Querier()
    {
        Util.setQueryMode(true);
        try
        {
            readInvertDoc();
        }
        catch (IOException e)
        {
            Logger.logException(e);
        }
        try
        {
            readPageRankInfo();
        }
        catch (Exception e)
        {
            Logger.logException(e);
        }
    }

    private void readInvertDoc() throws IOException
    {
    	InputStream in = new FileInputStream(Util.getInvertDocumentFile());
    	byte[] b = new byte[in.available()];
    	in.read(b);
    	String[] contents = new String(b, Charset.forName("UTF-8")).split("[\r\n]+");
    	for (String string : contents) 
    	{
            Term.addTermFromInvertDocString(string);	
		}
    }

    private void readPageRankInfo() throws FileNotFoundException
    {
        pageRanks = new HashMap<Integer, Double>();
        Scanner scan = new Scanner(Util.getPageRankFile());
        while (scan.hasNext())
        {
            int key = scan.nextInt();
            double value = scan.nextDouble();
            pageRanks.put(key, value);
        }
    }

    public synchronized Map<Document, Double> query(String queryStr,
            boolean withPageRank)
    {
        Document doc = new QueryStringParser(queryStr).parse();
        HashMap<Document, Double> tmpResult = new HashMap<Document, Double>();
        Set<Document> relativeDocuments = getAllRelativeDocuments(doc);
        double maxSim = -1;
        for (Document document : relativeDocuments)
        {
            double sim = getSimilarity(doc, document);
            if (withPageRank)
                sim *= pageRanks.get(document.getDocumentID());
            if (sim != 0 && document.getDocumentID() != -2)
                tmpResult.put(document, sim);
            if (sim > maxSim)
                maxSim = sim;
        }
        HashMap<Document, Double> result = new HashMap<Document, Double>();
        for (Document document : tmpResult.keySet())
        {
            result.put(document, tmpResult.get(document) / maxSim);
        }
        return result;
    }

    private double getSimilarity(Document queryDoc, Document doc)
    {
        Set<Term> queryContents = queryDoc.getContent();
        Set<Term> contents = doc.getContent();
        double maxQueryWeight = queryDoc.getMaxWeight();
        double sum = 0;
        for (Term term : queryContents)
        {
            if (contents.contains(term))
            {
                double qWeight = 0.5 + 0.5 * term.getWeight(queryDoc)
                        / maxQueryWeight;
                double dWeight = term.getWeight(doc);
                sum += qWeight * dWeight;
            }
        }
        sum /= queryDoc.getLength();
        sum /= doc.getLength();
        return sum;
    }

    private Set<Document> getAllRelativeDocuments(Document doc)
    {
        Set<Document> result = new HashSet<Document>();
        Set<Term> terms = doc.getContent();
        for (Term term : terms)
        {
            result.addAll(term.getAllDocuments());
        }
        return result;
    }

    public static void main(String[] args) throws IOException
    {
        Querier q = new Querier();
        final Map<Document, Double> result = q.query("中国", true);
        Document[] documents = result.keySet().toArray(new Document[0]);
        Arrays.sort(documents, new Comparator<Document>()
        {
            public int compare(Document o1, Document o2)
            {
                double w1 = result.get(o1);
                double w2 = result.get(o2);
                if (Math.abs(w1 / w2 - 1) < 1E-9)
                    return 0;
                if (w1 > w2)
                    return -1;
                return 1;
            }
        });
        for (Document d : documents)
        {
            System.out.println(d.getDocumentID() + " " + result.get(d));
        }
    }
}
